package com.junxian.springboot03.juc.threadPool;

import java.util.concurrent.*;

public class ThreadPool {

    public static void main(String[] args) {
        //自带线程工具类
//        executorsTest();

        //自定义线程工具类
        customThread();
    }

    /**
     * 自定义线程池: 三大方法 七大参数 四大拒绝策略
     * 1、在创建了线程池后，等待提交过来的任务请求。
     *
     * 2、当调用execute()方法添加一个任务请求，线程池会做如下判断：
     *
     * 2.1 如果正在运行的线程数小于或者等于corePoolSize，那么马上会创建线程运行这个任务；
     * 2.2 如果正在运行的线程数大于corePoolSize，那么会将这个任务放入队列；
     * 2.3 如果这时候队列满了并且正在运行的线程数量还小于maximumPoolSize，那么还是要创建非核心线程运行这个任务；
     * 2.4 如果队列满了并且线程数大于或者等于maximumPoolSize，那么会启动饱和拒绝策略来执行。
     * 3、当一个线程完成时，它会从队列中取下一个任务来执行。
     *
     * 4、当一个线程无事可做，且超过一定的时间（keepAliveTime）时，线程池会判断：
     *
     *        如果当前运行的线程数大于corePoolSize，那么这个线程会停掉。
     *
     *        所以线程池的所有任务完成后，它最终会收缩到corePoolSize的大小。
     */
    public static void customThread(){
        //例子 银行办理业务
        // corePoolSize 2; ==> 银行最开始常驻柜台办理员
        // maximumPoolSize:5 ; ==> BlockingQueue.size() >=3 之后（银行等待席位满了），后备最大柜台办理员数量
        // BlockingQueue:：3 ==> 银行等待席位数量
        // 拒绝策略L默认(抛异常) ,==> 后备柜台办理员也忙不过来，人越来越多了，就开始施行拒绝策略
        // ① new ThreadPoolExecutor.AbortPolicy() 在执行maximumPoolSize + BlockingQueue.size()线程后，其余线程报错 java.util.concurrent.RejectedExecutionException
        // ② new ThreadPoolExecutor.CallerRunsPolicy() 由哪里来回哪里去，将多来的线程回退到调用者(原来开始调用的那个线程,这里是main线程)调用
        // ③ new ThreadPoolExecutor.DiscardOldestPolicy()  抛弃队列中等待最久的任务，然后把当前任务加入队列中尝试再次提交当前任务。
        // ④ new ThreadPoolExecutor.DiscardPolicy() 直接丢弃任务，不予任何处理也不抛出异常。如果允许任务丢失
        // keepAliveTime(线程存活时间) 10s ==> 当前线程池数量超过corePoolSize时，当空闲时间到达keepAliveTime值时，多余空闲线程会被销毁直到只剩下corePoolSize个线程为止。
        // unit：keepAliveTime的时间单位
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                10,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy()
        );

        for (int i = 1; i <= 20; i++) {
            threadPoolExecutor.submit(()->{
                System.out.println(Thread.currentThread().getName() + " 准备就绪! ");
            });
        }
    }

    /**
     * 原有线程池测试类
     * 注意：线程池不允许使用Executors去创建，而是通过ThreadPoolExecutor的方式，这样的处理方式让写的同学更加明确线程池的运行规则，规避资源耗尽的风险。
     * 线程池方法允许请求的队列长度是Integer.MAX_VALUE(接近21亿)，会堆积大量请求，从而导致OOM
     */
    public static void executorsTest(){
//        ExecutorService executorService = Executors.newFixedThreadPool(5);//固定大小线程池
//        ExecutorService executorService = Executors.newSingleThreadExecutor();//单线程池
        ExecutorService executorService = Executors.newCachedThreadPool();//遇强则强，最大达到机器的最大性能

        for (int i = 1; i < 100; i++) {
            executorService.submit(()->{
                System.out.println(Thread.currentThread().getName() + " is ok!");
            });
        }
    }
}
